package com.nageoffer.jobtrain.common.framework.idempotent;

import com.nageoffer.jobtrain.common.web.exception.ClientException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 防止用户一分钟内重复申请验证码请求切面控制器
 * author：唐晨
 */
@Aspect
@Slf4j
@RequiredArgsConstructor
@Component
public final class NoSmsFrequentRequestAspect {
    private final StringRedisTemplate stringRedisTemplate;

    @Around("@annotation(com.nageoffer.jobtrain.common.framework.idempotent.NoSmsFrequentRequest)")
    public Object noSmsFrequentRequest(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        NoSmsFrequentRequest noSmsFrequentRequest = getNoSmsFrequentRequest(joinPoint);
        // 获取注解参数
        String keyPrefix = noSmsFrequentRequest.keyPrefix();
        String keyField = noSmsFrequentRequest.keyField();
        long minimumInterval = noSmsFrequentRequest.minimumInterval();
        long totalExpireTime = noSmsFrequentRequest.totalExpireTime();

        // 获取手机号
        Object[] args = joinPoint.getArgs();
        String phoneNumber = extractFieldValue(args, keyField);

        // 手机验证码缓存key
        String redisKey = keyPrefix + phoneNumber;

        // 检查 Redis 是否存在 Key，并获取剩余时间
        Long expireTime = stringRedisTemplate.getExpire(redisKey, TimeUnit.SECONDS);
        if (expireTime != null && expireTime > 0) {
            if (expireTime > totalExpireTime - minimumInterval) {
                long waitTime = expireTime - (totalExpireTime - minimumInterval);
                throw new ClientException("验证码已发送，请等待 " + waitTime + " 秒后重试！");
            }
        }
        // 执行目标方法
        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable ex) {
            log.error("手机号 {} 验证码发送失败！", phoneNumber, ex.getMessage());
            throw new ClientException("验证码发送失败，手机号为：" + phoneNumber);
        }
        return result;
    }

    /**
     * 通过反射提取方法参数中指定字段的值
     */
    private String extractFieldValue(Object[] args, String keyField) {
        for (Object arg : args) {
            try {
                Class<?> clazz = arg.getClass();
                while (clazz != null) {
                    try {
                        Field field = clazz.getDeclaredField(keyField);
                        field.setAccessible(true);
                        Object value = field.get(arg);
                        if (value instanceof String) {
                            return (String) value;
                        }
                    } catch (NoSuchFieldException e) {
                        // 如果当前类没有该字段，继续向父类查找
                        clazz = clazz.getSuperclass();
                    }
                }
            } catch (Exception ignored) {
            }
        }
        throw new ClientException("无法提取字段：" + keyField);
    }

    /**
     * @return 返回防止用户一分钟内重复申请验证码请求幂等性注解
     */
    private static NoSmsFrequentRequest getNoSmsFrequentRequest(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = joinPoint.getTarget().getClass()
                .getDeclaredMethod(methodSignature.getName(), methodSignature.getMethod().getParameterTypes());
        return targetMethod.getAnnotation(NoSmsFrequentRequest.class);
    }
}
